﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace XNAGuiLib.GuiLibAction
{
    public abstract class Action
    {
        //
        #region Non Public Field - _initialMouseDownInsideInterfaceObject;
        // flag for the check if the mouse is inside the bounds before the click down, pressed or released
        protected bool _initialMouseDownInsideInterfaceObject;
        #endregion
        //
        //
        #region Field - Enabled
        /// <summary>
        /// flag if the action is enabled or not (default: true)
        /// </summary>
        protected bool _enabled = true;
        /// <summary>
        /// gets or sets if this Action is Enabled
        /// </summary>
        public bool Enabled
        {
            get
            {
                return _enabled;
            }
            set
            {
                _enabled = value;
            }
        }
        #endregion
        //
        #region Field - Command
        /// <summary>
        /// command which should be executed when the action is invoked
        /// </summary>
        protected string _command;
        /// <summary>
        /// gets or sets  the Custom Command for this Action to execute on the target
        /// </summary>
        public string Command
        {
            get
            {
                return _command;
            }
            set
            {
                _command = value;
            }
        }
        #endregion
        //
        #region Field - Target
        /// <summary>
        /// the target InterfaceObject which will be the base for the command execution
        /// </summary>
        protected InterfaceObject _target;
        /// <summary>
        /// gets or sets  the InterfaceObject target for this Action
        /// </summary>
        public InterfaceObject Target
        {
            get
            {
                return _target;
            }
            set
            {
                _target = value;
            }
        }
        #endregion
        //
        #region Field - Type
        /// <summary>
        /// CommandType of this action object (see the enumeration below for details)
        /// </summary>
        protected CommandType _type;
        /// <summary>
        /// gets or sets the Command for this Action
        /// </summary>
        public CommandType Type
        {
            get
            {
                return _type;
            }
            set
            {
                _type = value;
            }
        }
        #endregion
        //
        #region Field - Device
        /// <summary>
        /// device which will invoke the action
        /// </summary>
        protected Devices _device;
        /// <summary>
        /// gets or sets the Device for this Action (mouse or key)
        /// </summary>
        public Devices Device
        {
            get
            {
                return _device;
            }
            set
            {
                _device = value;
            }
        }
        #endregion
        //
        #region Field - InvokerType
        /// <summary>
        /// Texture or Text which will be checked for requirements (mouse position etc.) to invoke this action
        /// </summary>
        protected Invoker _invoker;
        /// <summary>
        /// gets or sets  the InvokerType (Text/Texture) for this Action
        /// </summary>
        public Invoker InvokerType
        {
            get
            {
                return _invoker;
            }
            set
            {
                _invoker = value;
            }
        }
        #endregion
        //
        #region Field - InvokerInterfaceObject
        /// <summary>
        /// 
        /// </summary>
        protected InterfaceObject _invokerInterfaceObject;
        /// <summary>
        /// gets or sets 
        /// </summary>
        public InterfaceObject InvokerInterfaceObject
        {
            get
            {
                return _invokerInterfaceObject;
            }
            set
            {
                _invokerInterfaceObject = value;
            }
        }
        #endregion
        //
        #region Field - InvokerTexture
        /// <summary>
        /// reference to the TextureObject to be the invoker
        /// </summary>
        protected TextureObject _invokerTexture;
        /// <summary>
        /// gets or sets  the TextureObject which invokes this Action
        /// </summary>
        public TextureObject InvokerTexture
        {
            get
            {
                return _invokerTexture;
            }
            set
            {
                _invokerTexture = value;
            }
        }
        #endregion
        //
        #region Field - InvokerText
        /// <summary>
        /// reference to the TextObject to be the invoker
        /// </summary>
        protected TextObject _invokerText;
        /// <summary>
        /// gets or sets  the TextObject which invokes this Action
        /// </summary>
        public TextObject InvokerText
        {
            get
            {
                return _invokerText;
            }
            set
            {
                _invokerText = value;
            }
        }
        #endregion
        //
        #region Field - KeyModifier
        /// <summary>
        /// optional key modifier for this action to be checked for (ctrl, alt, shift and combinations)
        /// </summary>
        protected KeyModifiers _keyModifier;
        /// <summary>
        /// gets or sets the KeyModifier for this Action
        /// </summary>
        public KeyModifiers KeyModifier
        {
            get
            {
                return _keyModifier;
            }
            set
            {
                _keyModifier = value;
            }
        }
        #endregion
        //
        #region Field - MouseState
        // MouseState of current cycle
        protected MouseState _ms;
        /// <summary>
        /// gets the current MouseState used by this Action
        /// </summary>
        public MouseState MouseState
        {
            get
            {
                return _ms;
            }
        }
        #endregion
        //
        #region Field - OldMouseState
        // old MouseState (from last update cycle)
        protected MouseState _oms;
        /// <summary>
        /// gets the old MouseState used by this Action
        /// </summary>
        public MouseState OldMouseState
        {
            get
            {
                return _oms;
            }
        }
        #endregion
        //
        #region Field - KeyboardState
        // KeyboardState of current cycle
        protected KeyboardState _ks;
        /// <summary>
        /// gets the current KeyboardState used by this Action
        /// </summary>
        public KeyboardState KeyboardState
        {
            get
            {
                return _ks;
            }
        }
        #endregion
        //
        #region Field - OldKeyboardState
        // old KeyboardState (from last update cycle)
        protected KeyboardState _oks;
        /// <summary>
        /// gets the old KeyboardState used by this Action
        /// </summary>
        public KeyboardState OldKeyboardState
        {
            get
            {
                return _oks;
            }
        }
        #endregion
        //
        //
        #region Enumeration - CommandType
        public enum CommandType
        { 
            /// <summary>
            /// execute a custom command defined in the _target InterfaceObject
            /// </summary>
            Custom,
            /// <summary>
            /// Hide the InterfaceObject
            /// </summary>
            Show,
            /// <summary>
            /// Hide the InterfaceObject
            /// </summary>
            Hide,
            /// <summary>
            /// Toggle Visibility (true/false) for KeyboardInput
            /// </summary>
            ToggleVisibility,
            /// <summary>
            /// Free Movement for MouseInput (usable with KeyDown & KeyReleased (not Pressed)
            /// </summary>
            Move,
            /// <summary>
            /// Movement Up the Y-Axxis for KeyboardInput (KeyDown/KeyPressed)
            /// </summary>
            MoveUp,
            /// <summary>
            /// Movement Down the Y-Axxis for KeyboardInput (KeyDown/KeyPressed)
            /// </summary>
            MoveDown,
            /// <summary>
            /// Movement Left the Y-Axxis for KeyboardInput (KeyDown/KeyPressed)
            /// </summary>
            MoveLeft,
            /// <summary>
            /// Movement Right the Y-Axxis for KeyboardInput (KeyDown/KeyPressed)
            /// </summary>
            MoveRight
        }
        #endregion
        //
        #region Enumeration - Devices
        public enum Devices
        {
            /// <summary>
            /// mouse
            /// </summary>
            Mouse,
            /// <summary>
            /// keyboard
            /// </summary>
            Keyboard
        }
        #endregion
        //
        #region Enumeration - Invoker
        public enum Invoker
        {
            /// <summary>
            /// a TextureObject invokes this Action
            /// </summary>
            Texture,
            /// <summary>
            /// a TextObject invokes this Action
            /// </summary>
            Text,
            /// <summary>
            /// a InterfaceObject invokes this Action
            /// </summary>
            InterfaceObject
        }
        #endregion
        //
        #region Enumeration - KeyModifiers
        /// <summary>
        /// modifiers are only checked for key Down when installed on a Action
        /// </summary>
        public enum KeyModifiers
        { 
            /// <summary>
            /// no key modifier
            /// </summary>
            None,
            /// <summary>
            /// control key
            /// </summary>
            Ctrl,
            /// <summary>
            /// alt key
            /// </summary>
            Alt,
            /// <summary>
            /// shift key
            /// </summary>
            Shift,
            /// <summary>
            /// control and alt key
            /// </summary>
            CtrlAlt,
            /// <summary>
            /// control and shift key
            /// </summary>
            CtrlShift,
            /// <summary>
            /// control, alt and shift key
            /// </summary>
            CtrlAltShift,
            /// <summary>
            /// alt and shift key
            /// </summary>
            AltShift,

        }
        #endregion
        //
        //
        #region Constructor - Action()
        /// <summary>
        /// default empty constructor
        /// </summary>
        public Action()
        { }
        #endregion
        //
        // see MouseAction   (implementation) for details
        #region Abstract Method - CheckMouseButton(MouseState ms);
        public abstract void CheckMouseButton(MouseState ms);
        #endregion
        // see KeyAction   (implementation) for details
        #region Abstract Method - CheckKeyboard(KeyboardState ks);
        public abstract void CheckKeyboard(KeyboardState ks);
        #endregion
        //
        //
        //
        #region Method - Check(MouseState ms, KeyboardState ks)
        public virtual void Check(MouseState ms, KeyboardState ks)
        {
            // start check procedure if this InterfaceAction is enabled (default to true)
            if (_enabled && _target.IsInitialized)
            {
                _ks = Keyboard.GetState();
                _ms = Mouse.GetState();

                #region Check - InitialMouseDownInsideInterfaceObject
                if (MouseHandler.CheckCurrentOnePressedOldAllReleased(_ms, _oms) && CheckCoordinates(_ms)) 
                    _initialMouseDownInsideInterfaceObject = true;
                else if (MouseHandler.CheckCurrentAllReleasedOldOneReleased(_ms, _oms))
                    _initialMouseDownInsideInterfaceObject = false;
                #endregion

                if (CheckKeyModifier())
                {
                    #region Switch - Device
                    switch (_device)
                    {
                        case Devices.Mouse:
                            CheckMouseButton(_ms);

                            break;

                        case Devices.Keyboard:
                            CheckKeyboard(_ks);

                            break;
                    }
                    #endregion
                }

                _oks = _ks;
                _oms = _ms;
            }
        }
        #endregion
        // 
        // ============================================
        // Execute Methods for base Action Execution
        // ============================================
        // Execute custom command
        #region Method - Execute()
        public void Execute()
        {
            // Execute the custom command which was set to this action object
            _target.Execute(_command);
        }
        #endregion
        // Execute ToggleVisibility command
        #region Method - ExecuteActionToggleVisibility()
        public void ExecuteActionToggleVisibility()
        {
            // check if target is visible already
            if (_target.Visible)
            {
                // set the visibility to false
                _target.Visible = false;
                // loop for all child-objects inside the target
                foreach (InterfaceObject io in _target.InterfaceObjects)
                {
                    // set child visibility to false
                    io.Visible = false;
                }
            }
            // case if target is invisible
            else
            {
                //set the visibility to true
                _target.Visible = true;
                // loop for all child-objects inside the target
                foreach (InterfaceObject io in _target.InterfaceObjects)
                {
                    // set child visibility to true
                    io.Visible = true;
                }
            }
        }
        #endregion
        // Execute Hode command
        #region Method - ExecuteActionHide()
        public void ExecuteActionHide()
        {
            // check if target is visible
            if (_target.Visible)
            {
                // control output to console (commented)
                // Console.WriteLine("Hide:" + this._type + ", " + this._device + ", " + _target.Name);
                // set targst's visibility to false
                _target.Visible = false;
            }
        }
        #endregion
        // Execute Show command
        #region Method - ExecuteActionShow()
        public void ExecuteActionShow()
        {
            // check if target is invisible
            if (!_target.Visible)
            {
                // control output to console (commented)
                // Console.WriteLine("Show:" + this._type + ", " + this._device + ", " + _target.Name);
                // set target's visibility to true
                _target.Visible = true;
            }
        }
        #endregion
        // Execute Move command
        #region Method - ExecuteActionMove(MouseState ms, MouseState oms)
        public void ExecuteActionMove(MouseState ms, MouseState oms)
        {
            // check if target is visible (move will never work with a invisible interface component)
            if (_target.Visible)
            {
                // get the current mouse position
                Vector2 mousePos = new Vector2(ms.X, ms.Y);
                // get the mouse position from the last update cycle
                Vector2 mousePosOld = new Vector2(oms.X, oms.Y);
                // add the difference from (current mouse position and old mouse position) to the current target's position
                _target.Position += (mousePos - mousePosOld);
            }
        }
        #endregion
        // Execute MoveUp command
        #region Method - ExecuteActionMoveUp()
        public void ExecuteActionMoveUp()
        {
            // check if target is visible (move will never work with a invisible interface component)
            if (_target.Visible)
            {
                // control output to console (commented)
                // Console.WriteLine("MoveUp:" + this._type + ", " + this._device + ", " + _target.Name);
                // decrease the y-component of the target's position by 1
                _target.Position += new Vector2(0, -1);
            }
        }
        #endregion
        // Execute MoveDown command
        #region Method - ExecuteActionMoveDown()
        public void ExecuteActionMoveDown()
        {
            // check if target is visible (move will never work with a invisible interface component)
            if (_target.Visible)
            {
                // control output to console (commented)
                // Console.WriteLine("MoveDown:" + this._type + ", " + this._device + ", " + _target.Name);
                // increase the y-component of the target's position by 1
                _target.Position += new Vector2(0, 1);
            }
        }
        #endregion
        // Execute MoveLeft command
        #region Method - ExecuteActionMoveLeft()
        public void ExecuteActionMoveLeft()
        {
            // check if target is visible (move will never work with a invisible interface component)
            if (_target.Visible)
            {
                // control output to console (commented)
                // Console.WriteLine("MoveLeft:" + this._type + ", " + this._device + ", " + _target.Name);
                // decrease the x-component of the target's position by 1
                _target.Position += new Vector2(-1, 0);
            }
        }
        #endregion
        // Execute MoveRight command
        #region Method - ExecuteActionMoveRight()
        public void ExecuteActionMoveRight()
        {
            // check if target is visible (move will never work with a invisible interface component)
            if (_target.Visible)
            {
                // control output to console (commented)
                // Console.WriteLine("MoveRight:" + this._type + ", " + this._device + ", " + _target.Name);
                // increase the x-component of the target's position by 1
                _target.Position += new Vector2(1, 0);
            }
        }
        #endregion
        // 
        // ============================================
        // Enable / Disable Methods
        // ============================================
        // disable this action object
        #region Method - Disable()
        /// <summary>
        /// Disable the Action
        /// </summary>
        public void Disable()
        {
            // set enabled to false (means disabled)
            _enabled = false;
        }
        #endregion
        // enable this action object
        #region Method - Enable()
        /// <summary>
        /// Enable the Action
        /// </summary>
        public void Enable()
        {
            // set enabled to true
            _enabled = true;
        }
        #endregion
        //
        // ============================================
        // Check Methods for base Check Algorithm (Coordinates and KeyModifier)
        // ============================================
        // check the mouse coordinates with the invokers bounds
        #region Private Method - CheckCoordinates(Mousestate ms)
        protected bool CheckCoordinates(MouseState ms)
        {
            // Check if ActionInvoker is set to Texture or Text (or whatever still comes)
            if (_invoker == Invoker.Texture && _invokerTexture != null)
            {
                // Check if coordinated of the Mouse intersects the _textureInvoker Rectangle
                return _invokerTexture.CheckCoordinates(ms);
            }
            else if (_invoker == Invoker.Text && _invokerText != null)
            {
                // Check if coordinated of the Mouse intersects the _textureInvoker Rectangle
                return _invokerText.CheckCoordinates(ms);
            }
            else if (_invoker == Invoker.InterfaceObject && _invokerInterfaceObject != null)
            {
                return _invokerInterfaceObject.ResolveTotalRectangle().Intersects(new Rectangle(ms.X, ms.Y, 1, 1));
            }
            else return false;
        }
        #endregion
        // check the key modifiers to be pressed or not
        #region Private Method - CheckKeyModifier()
        protected bool CheckKeyModifier()
        {
            // check if key modifier is set to 'none'
            if (_keyModifier == KeyModifiers.None)
            {
                // return true
                return true;
            }
            // case if any modifier is set
            else
            {
                // switch for the different key modifier
                switch (_keyModifier)
                {
                    // case: key modifier set to 'ctrl'
                    case KeyModifiers.Ctrl:
                        // check if left ctrl or right ctrl is down in the current keyboard state
                        if (_ks.IsKeyDown(Keys.LeftControl) || _ks.IsKeyDown(Keys.RightControl))
                            return true;
                        break;
                    // case: key modifier set to 'ctrl+alt'
                    case KeyModifiers.CtrlAlt:
                        // check if left ctrl or right ctrl and left alt or right alt is down in the current keyboard state
                        if ((_ks.IsKeyDown(Keys.LeftControl) || _ks.IsKeyDown(Keys.RightControl))
                            && (_ks.IsKeyDown(Keys.LeftAlt) || _ks.IsKeyDown(Keys.RightAlt)))
                            return true;
                        break;
                    // case: key modifier set to 'ctrl+shift'
                    case KeyModifiers.CtrlShift:
                        // check if left ctrl or right ctrl and left shift or right shift is down in the current keyboard state
                        if ((_ks.IsKeyDown(Keys.LeftControl) || _ks.IsKeyDown(Keys.RightControl))
                            && (_ks.IsKeyDown(Keys.LeftShift) || _ks.IsKeyDown(Keys.RightShift)))
                            return true;
                        break;
                    // case: key modifier set to 'ctrl+shift+alt'
                    case KeyModifiers.CtrlAltShift:
                        // check if left ctrl or right ctrl and left shift or right shift and left alt or right alt is down in the current keyboard state
                        if ((_ks.IsKeyDown(Keys.LeftControl) || _ks.IsKeyDown(Keys.RightControl))
                            && (_ks.IsKeyDown(Keys.LeftAlt) || _ks.IsKeyDown(Keys.RightAlt))
                            && (_ks.IsKeyDown(Keys.LeftShift) || _ks.IsKeyDown(Keys.RightShift)))
                            return true;
                        break;
                    // case: key modifier set to 'alt'
                    case KeyModifiers.Alt:
                        // check if left alt or right alt is down in the current keyboard state
                        if (_ks.IsKeyDown(Keys.LeftAlt) || _ks.IsKeyDown(Keys.RightAlt))
                            return true;
                        break;
                    // case: key modifier set to 'alt+shift'
                    case KeyModifiers.AltShift:
                        // check if left alt or right alt and left shift or right shift is down in the current keyboard state
                        if ((_ks.IsKeyDown(Keys.LeftAlt) || _ks.IsKeyDown(Keys.RightAlt))
                            && (_ks.IsKeyDown(Keys.LeftShift) || _ks.IsKeyDown(Keys.RightShift)))
                            return true;
                        break;
                    // case: key modifier set to 'shift'
                    case KeyModifiers.Shift:
                        // check if left shift or right shift is down in the current keyboard state
                        if (_ks.IsKeyDown(Keys.LeftShift) || _ks.IsKeyDown(Keys.RightShift))
                            return true;
                        break;
                }
            }
            // for a 'default' returned is false when any of the cases doesn't match
            return false;
        }
        #endregion
    }
}